<?php
  /*
   Copyright (c) 2008   by  semka novikov <sdfgh153@8bit.perm.ru>,
   the-wheel-library team

   This program is free software; you can redistribute it and/or modify
   it under the terms of the GNU Lesser General Public License as published by
   the Free Software Foundation; either version 2 of the License, or
   (at your option) any later version.

   This program is distributed in the hope that it will be useful,
   but WITHOUT ANY WARRANTY; without even the implied warranty of
   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
   GNU General Public License for more details.

   You should have received a copy of the GNU Lesser General Public License
   along with this program; if not, write to the Free Software
   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */

class Fp{

  public static function lambda( $expression ) {
    return create_function(
			   self::composeLambdaArguments($expression),
			   self::composeLambdaBody($expression)
			   );
  }

  private static function composeLambdaArguments($expression) {

    // supported onesymbol variables
    // (?<![\\\\]) == "'\' symbol before pattern"
    preg_match_all('/(?<![\\\\])\$[a-zA-Z]\W/', $expression, $arrMatches);

    $arrUniqueArgs = array_unique( reset( $arrMatches ) );
    sort($arrUniqueArgs);

    return implode(',', $arrUniqueArgs);	
  }

  private static function composeLambdaBody($expression) {

    $sFuncBody = trim($expression);
    $sFuncBody = rtrim($expression, ';');

    $arrFuncBodyParts = explode(';', $sFuncBody);
    $sLastPart = end($arrFuncBodyParts);

    if (!preg_match('~return~i', $sLastPart)) {
      $arrFuncBodyParts[key($arrFuncBodyParts)] = 'return ' . $sLastPart;
      $sFuncBody = implode(';', $arrFuncBodyParts);
    }

    return $sFuncBody . ';';
  }

  /**
   * types: [a] -> f -> [a]
   * Применяет функцию fn ко всем элементам array.
   * Возвращает массив результатов обработки.
   * @$array: данные для обработки
   * @$fn: функция-обработчик
   */
  public static function map( $fn, $array ) {

    $retval = array();

    foreach( $array as $key=>$map_value ){

      if( is_array( $map_value ) ){
	$retval[$key] = self::map( $fn, $map_value );
      }
      else{
	$retval[$key] = $fn( $map_value );
      }
    }
    return $retval;
  }

  /**
   * types: [a] -> f -> a -> a
   * производит цепочечные вычисления,
   * многократно применяя данную функцию к каждому элементу,
   * подставляя аккумулятор в качестве первого параметра,
   * а сам элемент - в качестве второго.
   * При этом она берет от двух до трех аргументов:
   * функцию для вычисления и последовательность как обязательные
   * и стартовое значение аккумулятора как необязательный.
   */
  public static function reduce( $fn, $array, $accumulator = null ){

    foreach( $array as $reduce_value ){
      if( is_array( $reduce_value ) ){
	$accumulator = self::reduce( $fn, $reduce_value, $accumulator );
      }
      else{
	$accumulator = $fn( $accumulator, $reduce_value );
      }
    }
    return $accumulator;
  }

  /**
   * reduce alias
   */
  public static function fold( $fn, $array, $accumulator = null ){
    return FP::reduce( $fn, $array, $accumulator );
  }

  /**
   * types: [a] -> f -> [a]
   * Выполняет функцию $fn от каждого элемента массива,
   * в случае если функция возвращает true, добавляет значение
   * в результирующий массив.
   */
  public static function filter( $fn, $array ){

    $retval = array();

    foreach( $array as $key=>$filter_value ){
      if( is_array( $filter_value ) ){
	$retval[$key] = self::filter( $fn, $filter_value );
      }
      else
	if( $fn( $filter_value ) )
	  $retval[$key] = $filter_value;
    }
    return $retval;
  }

  public static function head( $l ){
    
    if( count($l) > 0 )    
      return $l[0];
    else
      return null;
  }

  public static function tail( $l ){

    $cdrValue = null;
    $arrLength = count( $l );
    
    if( $arrLength > 0 ){
      for( $i = 1; $i < $arrLength; $i++ ){
	$cdrValue[] = $l[$i];
      }
    }  
    
    return $cdrValue;
  }
  }
?>
